#include <stdio.h>
#include <string.h>
#include "tsocket.h"
#include "tbuffer.h"
tlib::TBufferPool* g_bufferPool = new tlib::TBufferPool;
namespace tlib
{
	struct STBlock
	{
		char buffer[BLOCK_SIZE];
	};
	TBufferPool::TBufferPool()
	{
		allBlocks_ = new STBlock[BLOCK_COUNT];
		for (int i = 0; i < BLOCK_COUNT; i++) noUseBlockNo_.push_back(i);
	}

	TBufferPool::~TBufferPool()
	{
		delete[] allBlocks_;
	}

	int TBufferPool::AllocBlock()
	{
		std::lock_guard<std::mutex> lock(mutex_);
		int blockNo = noUseBlockNo_.back();
		noUseBlockNo_.pop_back();
		return blockNo;
	}

	void TBufferPool::FreeBlock(int blockNo)
	{
		std::lock_guard<std::mutex> lock(mutex_);
		noUseBlockNo_.push_back(blockNo);
	}

	STBlock* TBufferPool::GetBlock(int blockNo)
	{
		STBlock* st = &allBlocks_[blockNo];
		return st;
	}

	TBuffer::TBuffer(TBufferPool* pool) : readIdx_(0), writeIdx_(0), readNo_(-1), writeNo_(-1)
	{
		bufferPool_ = pool;
	}

	TBuffer::~TBuffer()
	{
		for (int i = 0; i < blocks_.size(); i++)
		{
			bufferPool_->FreeBlock(blocks_[i]);
		}
	}

	bool TBuffer::Read(char* buff, int len, bool del)
	{
		std::lock_guard<std::mutex> lock(mutex_);
		if (blocks_.empty() || len <= 0) return false;
		if ((writeNo_*BLOCK_SIZE + writeIdx_) - (readNo_*BLOCK_SIZE + readIdx_) < len) return false;
		int oldReadNo = readNo_, oldReadIdx = readIdx_;
		for (; len > 0;)
		{
			STBlock* st = bufferPool_->GetBlock(blocks_[readNo_]);
			int ableReadLen = writeNo_ > readNo_ ? BLOCK_SIZE - readIdx_ : writeNo_ == readNo_ ? writeIdx_ - readIdx_ : 0;
			ableReadLen = ableReadLen > len ? len : ableReadLen;
			if (ableReadLen > 0)
			{
				memcpy(buff, &st->buffer[readIdx_], ableReadLen);
				buff += ableReadLen;
				len -= ableReadLen;
				readIdx_ += ableReadLen;
				if (readIdx_ == BLOCK_SIZE)
				{
					readNo_++;
					readIdx_ = 0;
				}
			}
		}
		if (del)
		{
			for (; readNo_ > 0 || (0 == readNo_ && 0 == writeNo_&&readIdx_ == writeIdx_);)
			{
				readNo_--;
				writeNo_--;
				bufferPool_->FreeBlock(*blocks_.begin());
				blocks_.erase(blocks_.begin());
			}
		}
		else
		{
			readNo_ = oldReadNo;
			readIdx_ = oldReadIdx;
		}
		return true;
	}

	bool TBuffer::Write(char* buff, int len)
	{
		std::lock_guard<std::mutex> lock(mutex_);
		if (blocks_.empty())
		{
			blocks_.push_back(bufferPool_->AllocBlock());
			readNo_ = 0;
			writeNo_ = 0;
		}
		for (; len > 0;)
		{
			STBlock* st = bufferPool_->GetBlock(blocks_[writeNo_]);
			int ableWriteLen = BLOCK_SIZE - writeIdx_;
			ableWriteLen = ableWriteLen > len ? len : ableWriteLen;
			if (ableWriteLen > 0)
			{
				memcpy(&st->buffer[writeIdx_], buff, ableWriteLen);
				buff += ableWriteLen;
				len -= ableWriteLen;
				writeIdx_ += ableWriteLen;
				if (writeIdx_ == BLOCK_SIZE && writeNo_ == blocks_.size() - 1)
				{
					blocks_.push_back(bufferPool_->AllocBlock());
					writeNo_++;
					writeIdx_ = 0;
				}
			}
		}
		return true;
	}

	int TBuffer::ReadFd(int srcfd, int len)
	{
		std::lock_guard<std::mutex> lock(mutex_);
		if (blocks_.empty())
		{
			blocks_.push_back(bufferPool_->AllocBlock());
			readNo_ = 0;
			writeNo_ = 0;
		}
		int realLen = 0;
		for (; realLen < len;)
		{
			STBlock* st = bufferPool_->GetBlock(blocks_[writeNo_]);
			int ableWriteLen = BLOCK_SIZE - writeIdx_;
			ableWriteLen = ableWriteLen > len ? len : ableWriteLen;
			if (ableWriteLen > 0)
			{
				int realWriteLen = TSocket::Recv(srcfd, &st->buffer[writeIdx_], ableWriteLen);
				if (realWriteLen <= 0) return realWriteLen == -1 ? -1 : realLen;
				realLen += realWriteLen;
				writeIdx_ += realWriteLen;
				if (writeIdx_ == BLOCK_SIZE && writeNo_ == blocks_.size() - 1)
				{
					blocks_.push_back(bufferPool_->AllocBlock());
					writeNo_++;
					writeIdx_ = 0;
				}
			}
		}
		return realLen;
	}
}